/*
 *2013.11.22.去掉冗余代码
 *author:xiaohao 2013.11.22
 *
 */
package com.geostar.core.dataset;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.util.ArrayList;

import com.geostar.core.common.jdbc.DBConnectionManager;
import com.geostar.core.dataset.extend.modify.IModifyConnInfo;
import com.geostar.core.dataset.extend.tool.DataSourceExtend;
import com.geostar.core.exception.DataSetException;
import com.geostar.core.exception.config.TextCodesConst;
import com.geostar.core.map.IObjectInfomation;
import com.geostar.core.map.ObjectTypeParser;
import com.geostar.kernel.GsConnectProperty;
import com.geostar.kernel.GsDataSourceType;

/**
 * 数据连接资源对象
 * 
 * @author 赵雷雷
 * 
 */
public class ConnectionInfo implements IObjectInfomation {
	/**
	 * 对应的连接池名称
	 */
	private String poolName;

	/**
	 * 数据连接地址
	 */
	private String address;

	/**
	 * 数据连接协议
	 */
	private ConnectionProtocol protocol;

	/**
	 * 预定义的连接池名
	 */
	private String predefinedPoolName;

	/**
	 * 连接串的前缀，与连接类型相关
	 */
	private String urlPrefix;
	/**
	 * 扩展属性
	 */
	private Map<String, String> properties = new HashMap<String, String>();

	/**
	 * 数据连接用户名
	 */
	private String username;

	/**
	 * 数据连接密码
	 */
	private String pw;

	/**
	 * 名称
	 */
	private String name;

	/**
	 * 版本
	 */
	private String version;

	/**
	 * 标题
	 */
	private String title;

	/**
	 * 描述
	 */
	private String description;

	public final static String[] geomesaConfigKeys = new String[]{
			"hbase.catalog",//数据集名  string 必填
			"hbase.connection",//hbase连接对象 org.apache.hadoop.hbase.client.Connection
			"hbase.zookeepers",//hbase连接地址 string
			"hbase.coprocessor.url",//包含协处理器的GeoMesa jar的路径 org.apache.hadoop.fs.Path
			"hbase.coprocessor.threads",//每个协处理器查询使用的HBase RPC线程数,默认值16   Integer
			"hbase.connections.reuse",//是否重用HBase连接，默认true  Boolean
			"hbase.remote.filtering",//禁用远程过滤和协处理器，用于无法安装自定义代码的环境，默认true  Boolean
			"hbase.ranges.max-per-extended-scan",//每个扩展扫描的最大范围值，默认100 Integer
			"hbase.ranges.max-per-coprocessor-scan",//每个协处理器扫描的最大值，默认Int.MaxValue Integer
			"hbase.security.enabled",//是否启用hbase 安全 Boolean
			"hbase.config.paths",//HBase附加的配置资源文件(逗号分隔) String
			"hbase.config.xml",//	额外的HBase配置属性  String
			"hbase.coprocessor.arrow.enable",//	是否将HBase分区服务器中的Arrow编码作为协处理器调用处理，默认true Boolean
			"hbase.coprocessor.bin.enable",//	作为协处理器调用处理HBase分区服务器的二进制编码，默认true  Boolean
			"hbase.coprocessor.density.enable",//默认true Boolean
			"hbase.coprocessor.stats.enable",//默认true Boolean
			"hbase.coprocessor.yield.partial.results"//默认false Boolean
	};
	private static List<String> geomesaConfigKeyList = new ArrayList<>();
	static {
		geomesaConfigKeyList = Arrays.asList(geomesaConfigKeys);
	}

	public enum HBaseKeyType{
		GEOMESA,
		HBASE
	}

	/**
	*@Author: yangjn
	*@Description: 获取Hbase的扩展属性
	*@date: 2022/7/6 10:12
	*@param keyType HBaseKeyType
	*@return: Map<String,String>
	*/
	public Map<String,String> getHBaseProperties(HBaseKeyType keyType){
		if(!ConnectionProtocol.getDataTypeInstance("HBASE").equals(protocol)){
			return null;
		}
		Map<String,String> geomesas = new HashMap<>();
		Map<String,String> hbases = new HashMap<>();
		for(Map.Entry<String,String> entry:properties.entrySet()){
			if(geomesaConfigKeyList.contains(entry.getKey())){
				geomesas.put(entry.getKey(),entry.getValue()) ;
			}else {
				hbases.put(entry.getKey(),entry.getValue()) ;
			}
		}
		if(HBaseKeyType.GEOMESA==(keyType)){
			return geomesas;
		}else {
			return hbases;
		}
	}

	/**
	 * 设置数据连接地址
	 * 
	 * @param address
	 *            数据连接地址
	 */
	public void setAddress(String address) {
		this.address = address;
	}

	/**
	 * <p>
	 * 根据连接字符串构建数据连接资源对象。
	 * </p>
	 * 
	 * <p>
	 * 连接字符串格式：<br>
	 * [数据源类型]:[]://([连接地址];[用户名];[密码])/[]<br>
	 * 或<br>
	 * [数据源类型]:[]://([连接地址];;)/[]<br>
	 * <br>
	 * 如：<br>
	 * 1. GeoGlobeFileSource:://(C:\SQLiteDB)/<br>
	 *
	 * 3. "HBaseSource://3.slave.spark,0.master.spark,1.slave.spark:namespace;root;keytab.file@@@sdafa;...";
	 * </p>
	 * 
	 * @param connInfoURL
	 *            连接字符串
	 */
	public ConnectionInfo(String connInfoURL) {
		try {// 解析协议
			int spIndex = connInfoURL.indexOf("//");
			String pString = connInfoURL.substring(0, spIndex);
			// 协议串，[数据源类型]:[数据类型]
			String[] pArray = pString.split(":");
			String protocolString = DataSourceInfo.protocolMap.get(pArray[0]);
			ConnectionProtocol protocol = ConnectionProtocol.getDataTypeInstance(protocolString);
			String address = null;
			String username = null;
			String pw = null;
			Map<String,String> maps = new HashMap<>();
			// 内容串，([连接地址];[用户名];[密码])/[数据空间名称]/[数据集名称]
			String context = connInfoURL.substring(spIndex + 1);
			if("HBASE".equals(protocolString)){
				String[] contents = context.split(";");
				address = contents[0].substring(1);
				if(contents.length>1){
					username = contents[1];
				}
				if(contents.length>2){
					for(int i=2;i<contents.length;i++){
						String keyValue = contents[i];
						maps.put(keyValue.split("@@@")[0],keyValue.split("@@@")[1]);
					}
				}
			}else {
				int beginIndex = context.indexOf('(');
				int endIndex = context.indexOf(')');
				String connStr = context.substring(beginIndex + 1, endIndex);
				// 解析连接地址、用户名、密码
				String[] connStrArray = connStr.split(";");

				int len = connStrArray.length;
				if (len >= 3) { // 若包含用户名与密码
					username = connStrArray[1];
					pw = connStrArray[2];
				}
				address = connStrArray[0];
			}
			// 构造对象
			setProtocol(protocol);
			setAddress(address);
			setConnectionIdentity(username, pw);
			//2018-9-28 添加poolName
			IModifyConnInfo modifyCon = DataSourceExtend.getModifyConnInfo(protocol);
			String poolName = modifyCon.modifyPoolName(username, address);
			setPoolName(poolName);
			setProperties(maps);
		} catch (Exception e) { // 连接协议不符合规范
			Object[] args = { connInfoURL };
			throw new DataSetException(ConnectionInfo.class,
					TextCodesConst.DATASET_CONNECTION_ERROR, args, e);
		}
	}

	public ConnectionInfo(GsConnectProperty gsConn){
		try{
			String address = gsConn.getServer()+":"+gsConn.getPort()+"/"+gsConn.getDatabase();
			this.address =address;
			this.username = gsConn.getUser();
			this.pw = gsConn.getPassword();
			int gsType = gsConn.getDataSourceType().swigValue();
			if(gsType==GsDataSourceType.ePostgreSQL.swigValue()){
				this.protocol = ConnectionProtocol.getDataTypeInstance("POSTGIS");
			}else if(gsType==GsDataSourceType.eMySQL.swigValue()){
				this.protocol = ConnectionProtocol.MYSQL;
			}
		}catch (Exception e){
			Object[] args = { gsConn };
			throw new DataSetException(ConnectionInfo.class,
					TextCodesConst.DATASET_CONNECTION_ERROR, args, e);
		}
	}
	public ConnectionInfo() {
	}

	/**
	 * 设置数据连接协议
	 * 
	 * @param protocol
	 *            数据连接协议
	 */
	public void setProtocol(ConnectionProtocol protocol) {
		this.protocol = protocol;
	}

	/**
	 * 设置数据连接认证信息，包含数据连接的用户名与密码。
	 * 
	 * @param username
	 *            数据连接用户名
	 * 
	 * @param password
	 *            数据连接密码
	 */
	public void setConnectionIdentity(String username, String password) {
		this.username = username;
		this.pw = password;
	}

	/**
	 * 返回数据连接地址
	 * 
	 * @return 数据连接地址
	 */
	public String getAddress() {
		return address;
	}

	/**
	 * 返回数据连接协议
	 * 
	 * @return 数据连接协议
	 */
	public ConnectionProtocol getProtocol() {
		return protocol;
	}

	/**
	 * 获取数据连接用户名
	 * 
	 * @return 数据连接用户名
	 */
	public String getUsername() {
		return username;
	}

	/**
	 * 获取数据连接密码
	 * 
	 * @return 数据连接密码
	 */
	public String getPassword() {
		return pw;
	}

	/**
	 * 判断与另一个数据连接资源对象是否一致
	 * 
	 * @param connectionInfo
	 *            另一个数据连接资源对象
	 */
	private boolean isEquals(ConnectionInfo connectionInfo) {

		if (null != this.protocol) { // 判断数据连接协议
			if (!this.protocol.equals(connectionInfo.getProtocol())) {
				return false;
			}
		} else if(null != connectionInfo.getProtocol()) {
			return false;
		}
		
		// 2013.11.29.增加address的判断,用于解决jdni类型的连接信息中没有address值导致的空指针异常
		if (null == this.address) {
			if (connectionInfo.getAddress() != null) {
				return false;
			}
		} else {
			if (!this.address.equals(connectionInfo.getAddress())) {// 判断数据连接地址
				return false;
			}
		}
		if (null == this.username) {
			if (connectionInfo.getUsername() != null) {
				return false;
			}
		} else {
			if (!this.username.equals(connectionInfo.getUsername())) { // 判断数据连接用户名
				return false;
			}
		}

		if (null != this.poolName && null != connectionInfo.getPoolName()) {// 判断连接池名称
			if (!this.poolName.equals(connectionInfo.getPoolName())) { 
				return false;
			}
		}
		
		if (null == this.pw) {
			if (connectionInfo.getPassword() != null) {
				return false;
			}
		} else {
			if (!this.pw.equals(connectionInfo.getPassword())) {// 判断数据连接密码
				 return false;
			}
		}
	

		if (null == this.properties || this.properties.size() == 0) {
			Map<String, String> properties = connectionInfo.getProperties();
			if (null != properties && properties.size() != 0) {
				return false;
			}
		} else {
			Map<String, String> properties = connectionInfo.getProperties();
			if (null != properties) {
				String preConnectionNum_Loca = this.properties
						.get("PreConnectionNum");
				String preConnectionNum = properties.get("PreConnectionNum");
				if ((null != preConnectionNum_Loca && null == preConnectionNum)|| (null == preConnectionNum_Loca
						&& null != preConnectionNum) || (null != preConnectionNum_Loca
						&& null != preConnectionNum
						&& !preConnectionNum_Loca
						.equalsIgnoreCase(preConnectionNum))){
					return false;
				}
				String maxConnectionNum_Loca = this.properties
						.get("PreConnectionNum");
				String maxConnectionNum = properties.get("PreConnectionNum");
				if( (null != maxConnectionNum_Loca && null == maxConnectionNum) || (null == maxConnectionNum_Loca
						&& null != maxConnectionNum)|| (null != maxConnectionNum_Loca
						&& null != maxConnectionNum
						&& !maxConnectionNum_Loca
						.equalsIgnoreCase(maxConnectionNum)) ){
					return false;
				}

				String timeOut_Loca = this.properties.get("PreConnectionNum");
				String timeOut = properties.get("PreConnectionNum");
				if ((null != timeOut_Loca && null == timeOut)||(null == timeOut_Loca && null != timeOut) || (null != timeOut_Loca && null != timeOut
						&& !timeOut_Loca.equalsIgnoreCase(timeOut)) ){
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 获取数据连接描述信息
	 */
	@Override
	public String getDescription() {
		return description;
	}

	/**
	 * 获取数据连接名称描述
	 */
	@Override
	public String getName() {
		return name;
	}

	/**
	 * 获取数据连接标题描述
	 */
	@Override
	public String getTitle() {
		return title;
	}

	/**
	 * 获取数据连接版本描述
	 */
	@Override
	public String getVersion() {
		return version;
	}

	/**
	 * 设置名称
	 * 
	 * @param name
	 *            名称
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * 设置版本
	 * 
	 * @param version
	 *            版本
	 */
	public void setVersion(String version) {
		this.version = version;
	}

	/**
	 * 设置标题
	 * 
	 * @param title
	 *            标题
	 */
	public void setTitle(String title) {
		this.title = title;
	}

	/**
	 * 设置描述信息
	 * 
	 * @param description
	 *            描述信息
	 */
	public void setDescription(String description) {
		this.description = description;
	}

	/**
	 * 返回扩展属性
	 * 
	 * @return 扩展属性
	 */
	public Map<String, String> getProperties() {
		return properties;
	}

	/**
	 * 设置扩展属性
	 * 
	 * @param properties
	 *            扩展属性
	 */
	public void setProperties(Map<String, String> properties) {
		if (null != properties) {
			this.properties = properties;
		}
	}

	/**
	 * 设置账号
	 * 
	 * @param username
	 *            账号
	 */
	public void setUsername(String username) {
		this.username = username;
	}

	/**
	 * 设置密码
	 * 
	 * @param password
	 *            密码
	 */
	public void setPassword(String password) {
		this.pw = password;
	}

	/**
	 * 设置对应的连接池名称
	 * 
	 * @param poolName
	 *            对应的连接池名称
	 */
	public void setPoolName(String poolName) {
		this.poolName = poolName;
	}

	/**
	 * 设置对应的连接池名称
	 * 
	 * @return 连接池名称
	 */
	public String getPoolName() {
		return this.poolName;
	}

	/**
	 * 名获取对应连接池的名称
	 * 
	 * @return 对应连接池的名称，如不存在对应的连接池则返回null
	 */
	public List<String> getMatchedPoolName() {
		DBConnectionManager dbcManager = null;
		dbcManager = DBConnectionManager.getInstance();

		if (null == dbcManager) {
			return null;
		}

		String signature = address;
		if (null == signature) {
			signature = "";
		}
		if (null == username) {
			signature = signature + ":";
		} else {
			signature = signature + ":" + username;
		}

		List<String> nameList = null;
		if (null == urlPrefix) {
			IDataBase db = null;
			try {
				db = DataBaseManager.getInstance().getDataBaseInstance(this);
			} catch (Exception ex) {
			}

			if (null != db) {
				urlPrefix = db.getURLPrefix();
			}
		}

		if (null != urlPrefix) {
			nameList = dbcManager.getMatchedPoolNames(urlPrefix + address,
					username);
		}
		
		// 20141211.1 搜索匹配连接池为空时，将"jdbc:oracle:thin:@127.0.0.1:1521:sid"格式的地址替换为"jdbc:oracle:thin:@127.0.0.1:1521/sid"再进行搜索
		if(null == nameList || nameList.isEmpty()) {
			int idx1 = address.lastIndexOf(':');
			int idx2 = address.lastIndexOf('/');
			if(idx2 < idx1) {
				try{
					String s1 = address.substring(0, idx1);
					String s2 = address.substring(idx1 + 1);
					String tmpaddress = s1 + "/" + s2;
					nameList = dbcManager.getMatchedPoolNames(urlPrefix + tmpaddress,
							username);
				} catch(Exception ex) {
				}
			}
		}
		return nameList;
	}

	@Override
	public boolean equals(Object obj) {
		if (obj == null) {
			return false;
		}

		if (obj instanceof ConnectionInfo) {
			return isEquals((ConnectionInfo) obj);
		}

		return false;
	}

	/**
	 * 获取预定义的连接池名
	 * 
	 * @return 连接池名
	 */
	public String getPredefinedPoolName() {
		return predefinedPoolName;
	}

	/**
	 * 设置预定义的连接池名
	 * 
	 * @param predefinedPoolName
	 *            预定义的连接池名
	 */
	public void setPredefinedPoolName(String predefinedPoolName) {
		this.predefinedPoolName = predefinedPoolName;
	}

	/**
	 * 获取连接串的前缀
	 * 
	 * @return 连接串的前缀
	 */
	public String getUrlPrefix() {
		if (null == urlPrefix) {
			IDataBase db = null;
			try {
				db = DataBaseManager.getInstance().getDataBaseInstance(this);
			} catch (Exception ex) {
			}

			if (null != db) {
				urlPrefix = db.getURLPrefix();
			}
		}
		return urlPrefix;
	}

	/**
	 * 设置连接串的前缀
	 * 
	 * @param urlPrefix
	 *            连接串的前缀
	 */
	public void setUrlPrefix(String urlPrefix) {
		this.urlPrefix = urlPrefix;
	}
	
	public String getSchema(){
		try{
			if(address != null){
				int index1 = address.lastIndexOf('/');
				int index2 = address.lastIndexOf(':');
				int index = index1>index2?index1:index2;
				return address.substring(index+1);
			}else{
				return null;
			}
		}catch(Exception e){
			return null;
		}
	}
}
